package nl.ceasaro.jeslee.core.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import nl.ceasaro.jeslee.core.exception.NotFoundException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;

public abstract class AbstractHibernateDAO<T, ID extends Serializable>
      extends AbstractDAO<T, ID>
{
   Log log = LogFactory.getLog(AbstractHibernateDAO.class);

   private SessionFactory sessionFactory;

   protected Session getSession()
   {
      Session session = null;
      try
      {
         session = getSessionFactory().getCurrentSession();
      }
      catch (HibernateException e)
      {
         log.warn("unable to get current hibernate session, opening a new one.", e);
         try
         {
            session = getSessionFactory().openSession();
         }
         finally
         {
            if (session != null)
            {
               session.close();
            }
         }
      }
      return session;
   }

   protected Criteria createCriteria()
   {
      return getSession().createCriteria(getPojoClass());
   }

   @SuppressWarnings("unchecked")
   @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
   public List<T> findAll()
   {
      return createCriteria().list();
   }

   @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
   public int size()
   {
      Criteria crit = createCriteria();
      crit.setProjection(Projections.rowCount());
      return (Integer) crit.uniqueResult();
   }

   @SuppressWarnings("unchecked")
   @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
   public T findById(ID id)
   {
      return (T) getSession().get(getPojoClass(), id);
   }

   @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
   public T get(ID id) throws NotFoundException
   {
      T e = findById(id);
      if (e != null)
      {
         return e;
      }
      throw new NotFoundException(new StringBuilder("Did not find entity with id: ").append(id).toString());
   }

   @SuppressWarnings("unchecked")
   @Transactional(propagation = Propagation.REQUIRED)
   public ID save(T instance)
   {
      if (log.isDebugEnabled())
      {
         log.debug(new StringBuilder("saving: ").append(instance.toString()).toString());
      }
      return (ID) getSession().save(instance);
   }

   @Transactional(propagation = Propagation.REQUIRED)
   public void update(T instance)
   {
      if (log.isDebugEnabled())
      {
         log.debug(new StringBuilder("updating: ").append(instance).toString());
      }
      getSession().update(instance);
   }

   @Transactional(propagation = Propagation.REQUIRED)
   public void delete(T instance)
   {
      getSession().delete(instance);
   }

   public SessionFactory getSessionFactory()
   {
      return sessionFactory;
   }

   @Resource
   public void setSessionFactory(SessionFactory sessionFactory)
   {
      this.sessionFactory = sessionFactory;
   }


}
